import os
import threading
import random
import shutil
from typing import Iterable

write_lock = threading.Lock()


def is_empty(path):
    if os.path.exists(path):
        return os.stat(path).st_size == 0
    return True


replace_lock = threading.Lock()


def replace(path, old, new):
    with replace_lock:
        with open(path) as f:
            new = f.read().replace(old, new)
        with open(path, "w") as f:
            f.write(new)


def read(path):
    text = None
    try:
        with open(path, 'r', encoding='utf-8') as f:
            text = f.read()
    except:
        pass
    return text


def write(path, data, append=False):
    os.makedirs(os.path.dirname(os.path.abspath(path)), exist_ok=True)
    try:
        with open(path, 'a' if append else 'w', encoding='utf-8') as f:
            f.write(data)
    except:
        pass


def write_sync(path, data, append=False):
    os.makedirs(os.path.dirname(os.path.abspath(path)), exist_ok=True)
    with write_lock:
        try:
            with open(path, 'a' if append else 'w', encoding='utf-8') as f:
                f.write(data)
        except:
            pass


def endswith_newline(path):
    try:
        filesize = os.stat(path).st_size
        if filesize == 0:
            return True
        with open(path, 'r', encoding='utf-8') as f:
            f.seek(filesize - 1)
            line = f.readline()
            if line == '\n':
                return True
    except:
        pass
    return False


def read_random_line(path: str):
    try:
        with open(path, 'r', encoding='utf-8') as f:
            return random.choice(list(filter(bool, map(str.strip, f.readlines()))))
    except:
        pass
    return None


def read_and_delete_first_line(path):
    lines = read_and_delete(path)
    if lines:
        return lines[0]
    return None


def read_and_delete(path, n: int = 1, start: int = 0) -> list:
    with write_lock:
        try:
            with open(path, 'r', encoding='utf-8') as f:
                lines = f.readlines()
            with open(path, 'w', encoding='utf-8') as f:
                f.writelines(lines[0:start])
                f.writelines(lines[start + n:])
            return [line.strip() for line in lines[start:start + n]]
        except Exception as e:
            pass
    return []


def read_to_list(path):
    content = []
    try:
        with open(path, 'r', encoding='utf-8') as f:
            content = list(set(f.read().splitlines()))
    except Exception as e:
        pass
    return content


def write_list(path, data: Iterable, append: bool = False):
    os.makedirs(os.path.dirname(os.path.abspath(path)), exist_ok=True)
    try:
        with open(path, 'a' if append else 'w', encoding='utf-8') as f:
            for line in data:
                f.write(str(line) + '\n')
    except:
        pass


def remove_files(dir):
    for filename in os.listdir(dir):
        file_path = os.path.join(dir, filename)
        try:
            if os.path.isfile(file_path) or os.path.islink(file_path):
                os.unlink(file_path)
            else:
                shutil.rmtree(file_path)
        except:
            pass


def _blocks(files, size=65536):
    while True:
        b = files.read(size)
        if not b: break
        yield b


def get_line_num(path: str) -> int:
    if os.path.exists(path):
        with open(path, "rbU") as f:
            return sum(1 for _ in f)
    return 0


def get_line_num_sync(path: str) -> int:
    if os.path.exists(path):
        with write_lock:
            with open(path, "r", encoding="utf-8", errors='ignore') as f:
                return sum(bl.count("\n") for bl in _blocks(f))
    return 0
